﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Resources;
using System.IO;
using System.Configuration;

namespace IGuess
{
    public class MAPClassifier : Classifier
    {

        #region Attributes

        private int numberOfWords;
        private ResourceManager RM = new ResourceManager("IGuess.GlobalResources", System.Reflection.Assembly.GetExecutingAssembly());
        private Hashtable htEN = new Hashtable();
        private Hashtable htFR = new Hashtable();
        private Hashtable htDE = new Hashtable();
        private string result;
        private Double scoreEN = 0;
        private Double scoreFR = 0;
        private Double scoreDE = 0;

        #endregion

        #region Properties

        public int NumberOfWords
        {
            get { return this.numberOfWords; }
            set { this.numberOfWords = value; }
        }

        public string Result
        {
            get { return this.result; }
            set { this.result = value; }
        }

        public Hashtable HtEN
        {
            get { return this.htEN; }
            set { this.htEN = value; }
        }

        public Hashtable HtFR
        {
            get { return this.htFR; }
            set { this.htFR = value; }
        }

        public Hashtable HtDE
        {
            get { return this.htDE; }
            set { this.htDE = value; }
        }

        public Double ScoreEN
        {
            get { return this.scoreEN; }
            set { this.scoreEN = value; }
        }

        public Double ScoreFR
        {
            get { return this.scoreFR; }
            set { this.scoreFR = value; }
        }

        public Double ScoreDE
        {
            get { return this.scoreDE; }
            set { this.scoreDE = value; }
        }

        #endregion

        #region Constructors

        public MAPClassifier(string text)
        {
            this.text = text;
            this.NumberOfWords = this.wordsInAText(text);
        }

        public MAPClassifier() { }

        #endregion

        #region methods

        public override string Classify()
        {
            //Calculate the normal distribution for each characteristic

            //German
            Double deA = getNormalDistribution("a", "de");
            Double deE = getNormalDistribution("e", "de");
            Double deI = getNormalDistribution("i", "de");
            Double deN = getNormalDistribution("n", "de");
            Double deO = getNormalDistribution("o", "de");
            Double deR = getNormalDistribution("r", "de");
            Double deS = getNormalDistribution("s", "de");
            Double deT = getNormalDistribution("t", "de");

            //French
            Double frA = getNormalDistribution("a", "fr");
            Double frE = getNormalDistribution("e", "fr");
            Double frI = getNormalDistribution("i", "fr");
            Double frN = getNormalDistribution("n", "fr");
            Double frO = getNormalDistribution("o", "fr");
            Double frR = getNormalDistribution("r", "fr");
            Double frS = getNormalDistribution("s", "fr");
            Double frT = getNormalDistribution("t", "fr");

            //English
            Double enA = getNormalDistribution("a", "en");
            Double enE = getNormalDistribution("e", "en");
            Double enI = getNormalDistribution("i", "en");
            Double enN = getNormalDistribution("n", "en");
            Double enO = getNormalDistribution("o", "en");
            Double enR = getNormalDistribution("r", "en");
            Double enS = getNormalDistribution("s", "en");
            Double enT = getNormalDistribution("t", "en");

            //Associate each characteristic with a value from the normal distribution

            //English
            this.htEN.Add("a", enA);
            this.htEN.Add("e", enE);
            this.htEN.Add("i", enI);
            this.htEN.Add("n", enN);
            this.htEN.Add("o", enO);
            this.htEN.Add("r", enR);
            this.htEN.Add("s", enS);
            this.htEN.Add("t", enT);

            //German
            this.htDE.Add("a", deA);
            this.htDE.Add("e", deE);
            this.htDE.Add("i", deI);
            this.htDE.Add("n", deN);
            this.htDE.Add("o", deO);
            this.htDE.Add("r", deR);
            this.htDE.Add("s", deS);
            this.htDE.Add("t", deT);

            //French
            this.htFR.Add("a", frA);
            this.htFR.Add("e", frE);
            this.htFR.Add("i", frI);
            this.htFR.Add("n", frN);
            this.htFR.Add("o", frO);
            this.htFR.Add("r", frR);
            this.htFR.Add("s", frS);
            this.htFR.Add("t", frT);

            //Sum the values from each characteristic to each language to a final score
            this.scoreEN = enA + enE + enI + enN + enO + enR + enS + enT;
            this.scoreDE = deA + deE + deI + deN + deO + deR + deS + deT;
            this.scoreFR = frA + frE + frI + frN + frO + frR + frS + frT;

            //Create an array of 3x1 with the three scores
            List<Double> scores = new List<Double>();
            scores.Add(scoreDE);
            scores.Add(scoreEN);
            scores.Add(scoreFR);

            //Search for the minimum value of the multiplied array and return the result
            if (scores.Max() == scoreDE)
            {
                this.Result = "DE";
                return RM.GetString("FrequenceAlgorithmResult") + RM.GetString("DETextLanguage") + ".";
            }
            else if (scores.Max() == scoreEN)
            {
                this.Result = "EN";
                return RM.GetString("FrequenceAlgorithmResult") + RM.GetString("ENTextLanguage") + ".";
            }
            else if (scores.Max() == scoreFR)
            {
                this.Result = "FR";
                return RM.GetString("FrequenceAlgorithmResult") + RM.GetString("FRTextLanguage") + ".";
            }
            else
            {
                this.Result = "NA";
                return RM.GetString("FrequenceAlgorithmUnknownResult");
            }

        }

        public int CountStringOccurrences(string text, string pattern)
        {
            // Loop through all instances of the string 'text'.
            int count = 0;
            int i = 0;
            while ((i = text.ToLower().IndexOf(pattern.ToLower(), i)) != -1)
            {
                i += pattern.Length;
                count++;
            }
            return count;
        }

        private int wordsInAText(string text)
        {

            char[] delimit = new char[] { ' ' };
            string s10 = text;

            this.numberOfWords = s10.Split(delimit).Length;

            return this.NumberOfWords;
        }

        private int CountNonSpaceChars(string text)
        {
            int result = 0;
            foreach (char c in text)
            {
                if (!char.IsWhiteSpace(c))
                {
                    result++;
                }
            }
            return result;
        }

        public double getNormalDistribution(string characteristic, string language)
        {

            Double characteristicOccurences = CountStringOccurrences(this.text, characteristic);

            string[,] hs = new string[8, 3];

            if (File.Exists(ConfigurationManager.AppSettings[language + "_PopulationFile"]))
            {
                using (System.IO.StreamReader sr = new System.IO.StreamReader(ConfigurationManager.AppSettings[language + "_PopulationFile"]))
                {
                    string line;
                    int i = 0;
                    while ((line = sr.ReadLine()) != null)
                    {

                        string[] token = line.Split();

                        hs[i, 0] = token[0];
                        hs[i, 1] = token[1];
                        hs[i, 2] = token[2];

                        i++;
                    }

                }
            }

            Double characteristicPopulationalAverage = 0;
            Double characteristicVariance = 0;

            for (int i = 0; i < hs.GetLength(0); i++)
            {

                if (hs[i, 0].Equals(characteristic))
                {

                    characteristicPopulationalAverage = Convert.ToDouble(hs[i, 1].ToString());
                    characteristicVariance = Convert.ToDouble(hs[i, 2].ToString());

                }

            }

            Double characteristicAverage = characteristicOccurences / this.text.Length;

            Double result = (1 / (Math.Sqrt(2 * Math.PI * characteristicVariance))) * Math.Exp(-(Math.Pow((characteristicAverage - characteristicPopulationalAverage), 2)) / (2 * characteristicVariance));

            if (!double.IsNaN(result))
                return result;
            else
                return 0;

        }

        #endregion
    }
}
